package io.mycat.encrypt.impl;

import io.mycat.config.model.encrypt.EncryptColumn;
import io.mycat.config.model.encrypt.EncryptDataSource;
import io.mycat.config.model.encrypt.EncryptHelper;
import io.mycat.config.model.encrypt.EncryptTablePk;
import io.mycat.encrypt.EncryptAutoHandler;
import io.mycat.encrypt.constant.DbType;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.Map;

/**
 * @ClassName MysqlEncryptAutoHandler
 * @Author zrx
 * @Date 2022/8/26 10:09
 */
public class OracleEncryptAutoHandler implements EncryptAutoHandler {

	private static final Logger LOGGER = LoggerFactory.getLogger("OracleEncryptAutoHandler");

	@Override
	public void encryptAuto(EncryptDataSource dataSource) {
		LOGGER.info("encrypt auto schedule start!");
		try {
			Connection conn = null;
			try {
				String url = DbType.ORACLE.getUrl().replace("${host}", dataSource.getIp()).replace("${port}", dataSource.getPort()).replace("${sid}", dataSource.getSid());
				Class.forName(DbType.ORACLE.getDriveClassName());
				DriverManager.setLoginTimeout(10);
				conn = DriverManager.getConnection(url, dataSource.getUsername(),
						dataSource.getPassword());
				//获取需要加密的列
				Map<String, EncryptColumn> encryptColumnMap = dataSource.getEncryptColumnMap();
				for (Map.Entry<String, EncryptColumn> columnEntry : encryptColumnMap.entrySet()) {
					EncryptColumn column = columnEntry.getValue();
					String columnName = column.getName();
					List<EncryptTablePk> encryptTablePks = column.getEncryptTablePks();
					//遍历表，加密
					for (EncryptTablePk encryptTablePk : encryptTablePks) {
						PreparedStatement pst = null;
						ResultSet rs = null;
						String table = encryptTablePk.getName();
						String pk = encryptTablePk.getPk();
						String countSql = "SELECT COUNT(1) FROM " + table + " WHERE " + columnName + " NOT LIKE '!zl%'";
						long count;
						try {
							pst = conn.prepareStatement(countSql);
							//获取个数
							rs = pst.executeQuery();
							rs.next();
							count = rs.getLong(1);
						} finally {
							if (rs != null) {
								rs.close();
							}
							if (pst != null) {
								pst.close();
							}
						}
						//构建sql
						String baseSql = "SELECT " + columnName + "," + pk + " FROM " + table + " WHERE " + columnName + " NOT LIKE '!zl%' AND ROWNUM <= ";
						int limit = 1000;
						long page = count % limit == 0 ? count / limit : count / limit + 1;
						for (int i = 0; i <= page; i++) {
							try (PreparedStatement selpst = conn.prepareStatement(baseSql + limit); ResultSet selrs = selpst.executeQuery()) {
								while (selrs.next()) {
									PreparedStatement uppst = null;
									String colVal = selrs.getString(columnName);
									Object pkVal = selrs.getObject(pk);
									//如果被加密的字段值不为空并且不以 !zl 开头
									if (StringUtils.isNotEmpty(colVal)) {
										try {
											String updateSql = "UPDATE " + table + " SET " + columnName + "=? WHERE " + pk + "=?";
											uppst = conn.prepareStatement(updateSql);
											uppst.setString(1, EncryptHelper.hexWithPre(EncryptHelper.encode(colVal.getBytes(StandardCharsets.UTF_8))));
											uppst.setObject(2, pkVal);
											uppst.execute();
										} finally {
											if (uppst != null) {
												uppst.close();
											}
										}
									}
								}
							}
						}
					}
				}
			} finally {
				if (conn != null) {
					conn.close();
				}
			}
		} catch (Exception e) {
			LOGGER.error("encrypt auto schedule error", e);
		}
		LOGGER.info("encrypt auto schedule end!");
	}
}
